﻿using System;
using System.Collections.Generic;
using Possan.WebServer;
using System.IO;
using System.Net;
using Kioskvalt.MidiSequencer;
using Norkay.Utilities;
using Kioskvalt.Common;
using System.Threading;
using Utilities = Kioskvalt.MidiSequencer.Utilities;

namespace Kioskvalt.Server
{
	public class WebServerInterface : BaseWebServer
	{
		public override void HandleRequest(WebContext context)
		{
			Console.WriteLine("got request (#" + Thread.CurrentThread.ManagedThreadId + ") for: " + context.Request.LocalPath);
			if (context.Request.LocalPath == "/")
			{
				HandleRootRequest(context);
			}
			else if (context.Request.LocalPath == "/c.aspx" ||
				context.Request.LocalPath == "/api/call")
			{
				HandleAjaxRequest(context);
			}
			else if (context.Request.LocalPath.EndsWith(".js") ||
				context.Request.LocalPath.EndsWith(".jsdata") ||
				context.Request.LocalPath.StartsWith("/api"))
			{
				if (!HandleDynamicStaticContentRequest(context))
					HandleStaticContentRequest(context);
			}
			else
			{
				HandleStaticContentRequest(context);
			}

			context.Response.Flush();
			context.Response.End();
			// Console.WriteLine("request finished.");
		}

		private static bool HandleDynamicStaticContentRequest(WebContext context)
		{
			bool ret = false;
			Song s = State.CurrentSong;

			if (context.Request.LocalPath == "/patterns.jsdata" ||
				context.Request.LocalPath == "/api/patterns.js")
			{
				ListPatternsJSON(context, s);
				ret = true;
			}
			else if (context.Request.LocalPath == "/instruments.jsdata" ||
				context.Request.LocalPath == "/api/instruments.js")
			{
				ListInstrumentsJSON(context, s);
				ret = true;
			}
			else if (context.Request.LocalPath == "/tracks.jsdata" ||
			   context.Request.LocalPath == "/api/tracks.js")
			{
				ListTracksJSON(context, s);
				ret = true;
			}
			else if (context.Request.LocalPath == "/mididevices.jsdata" ||
				context.Request.LocalPath == "/api/mididevices.js")
			{
				ListMidiDevicesJSON(context, s);
				ret = true;
			}
			else if (context.Request.LocalPath == "/song.jsdata" ||
				context.Request.LocalPath == "/api/song.js")
			{
				ListSongJSON(context, s);
				ret = true;
			}
			else if (context.Request.LocalPath == "/automationchannels.jsdata" ||
				context.Request.LocalPath == "/api/automationchannels.js")
			{
				ListAutomationChannelsJSON(context, s);
				ret = true;
			}
			else if (context.Request.LocalPath == "/api/deviceuis.js")
			{
				ListDevicesUiJSON(context, s);
				ret = true;
			}
			else if (context.Request.LocalPath == "/api/mididevices.js")
			{
				ListMidiDevicesJSON(context, s);
				ret = true;
			}
			else if (context.Request.LocalPath == "/api/editoruis.js")
			{
				ListEditorsUiJSON(context, s);
				ret = true;
			}
			else if (context.Request.LocalPath == "/api/notifications")
			{
				PollLongRunning(context);
				ret = true;
			}

			if (ret)
			{

				context.Response.SetHeader("Expires", DateTime.Now.AddYears(-1).ToString("R"));
				context.Response.StatusCode = 200;
				context.Response.ContentType = "text/javascript";
			}

			return ret;
		}

		private static void ListSongJSON(WebContext context, Song s)
		{
			JSONWriter jw = new JSONWriter();
			jw.Class();
			jw.Field("bpm", s.BPM);
			jw.Field("beats1", s.Beats1);
			jw.Field("beats2", s.Beats2);
			jw.End();

			context.Response.Write("g_Song = ");
			context.Response.Write(jw.ToString(JSONFormatting.Pretty));
			context.Response.Write(";\n");
		}

		private static void ListTracksJSON(WebContext context, Song s)
		{
			JSONWriter jw = new JSONWriter();
			jw.Array();

			for (int j = 0; j < s.Tracks.Count; j++)
			{
				SongTrack st = s.Tracks[j];
				jw.Class();
				jw.Field("id", st.ID);
				jw.Field("name", st.Name);

				if (st.CurrentPatternID != null)
					jw.Field("pattern", st.CurrentPatternID);
				else
					jw.Field("pattern", "");

				if (st.CuedPatternID != null)
					jw.Field("cued", st.CuedPatternID);
				else
					jw.Field("cued", "");

				jw.Field("volume", st.Volume);
				jw.Field("transpose", st.Transpose);
				jw.Field("mute", st.Muted ? 1 : 0);
				jw.Field("solo", st.Solo ? 1 : 0);
				jw.End();
			}

			jw.End();

			context.Response.Write("g_Tracks = ");
			context.Response.Write(jw.ToString(JSONFormatting.Pretty));
			context.Response.Write(";\n");
		}

		private static void ListAutomationChannelsJSON(WebContext context, Song s)
		{
			JSONWriter jw = new JSONWriter();
			jw.Array();

			for (int j = 0; j < s.Tracks.Count; j++)
			{
				// SongTrack st = s.Tracks[j];
				jw.Class();
				jw.Field("id", j);
				jw.Field("name", "");
				jw.End();
			}

			jw.End();

			context.Response.Write("g_AutomationTracks = ");
			context.Response.Write(jw.ToString(JSONFormatting.Pretty));
			context.Response.Write(";\n");
		}


		private static void ListDevicesUiJSON(WebContext context, Song s)
		{
			JSONWriter jw = new JSONWriter();
			jw.Array();


			for (int j = 0; j < Configuration.Current.Devices.Count; j++)
			{
				DeviceConfig dev = Configuration.Current.Devices[j];
				jw.Class();
				jw.Field("id", dev.Name);
				jw.Field("name", dev.Description);
				jw.Field("width", dev.Width);
				jw.Field("height", dev.Height);
				jw.Array("uis");
				for (int k = 0; k < dev.UIs.Count; k++)
					jw.Field("", dev.UIs[k]);
				jw.End();
				jw.End();
			}

			jw.End();

			context.Response.Write("g_DeviceUIs = ");
			context.Response.Write(jw.ToString(JSONFormatting.Pretty));
			context.Response.Write(";\n");
		}


		private static void ListEditorsUiJSON(WebContext context, Song s)
		{


			JSONWriter jw = new JSONWriter();
			jw.Array();

			for (int j = 0; j < Configuration.Current.Editors.Count; j++)
			{
				EditorConfig ed = Configuration.Current.Editors[j];

				// SongTrack st = s.Tracks[j];
				jw.Class();
				jw.Field("id", ed.Name);
				jw.Field("name", ed.Description);
				jw.Field("width", ed.Width);
				jw.Field("height", ed.Height);
				jw.Array("uis");
				for (int k = 0; k < ed.UIs.Count; k++)
					jw.Field("", ed.UIs[k]);
				jw.End();
				jw.End();
			}

			jw.End();

			context.Response.Write("g_EditorUIs = ");
			context.Response.Write(jw.ToString(JSONFormatting.Pretty));
			context.Response.Write(";\n");
		}





		private static void ListInstrumentsJSON(WebContext context, Song s)
		{
			JSONWriter jw = new JSONWriter();
			jw.Array();

			for (int k = 0; k < s.Instruments.Count; k++)
			{
				Instrument ins = s.Instruments[k];

				jw.Class();
				jw.Field("id", ins.ID);
				jw.Field("name", ins.Name);
				jw.Field("device", ins.MidiDevice);
				jw.Field("channel", ins.MidiChannel);
				jw.Field("patch", ins.MidiPatch);
				jw.Field("type", ins.Type);
				jw.End();
			}

			jw.End();

			context.Response.Write("g_Instruments = ");
			context.Response.Write(jw.ToString(JSONFormatting.Pretty));
			context.Response.Write(";\n");
		}

		private static void ListPatternsJSON(WebContext context, Song s)
		{
			JSONWriter jw = new JSONWriter();
			jw.Array();

			for (int j = 0; j < s.Patterns.Count; j++)
			{
				Pattern p = s.Patterns[j];

				jw.Class();

				jw.Field("id", p.ID);
				jw.Field("instrument", p.InstrumentID);
				jw.Field("name", p.Name);
				jw.Field("duration", p.Duration);

				jw.Array("notes");
				for (int k = 0; k < p.Notes.Count; k++)
				{
					PatternNote pn = p.Notes[k];
					jw.Class();
					jw.Field("id", pn.ID);
					jw.Field("from", pn.From);
					jw.Field("to", pn.To);
					jw.Field("note", pn.Note);
					jw.Field("velocity", pn.Velocity);
					jw.End();
				}
				jw.End();

				jw.Array("automations");
				for (int k = 0; k < p.Automations.Count; k++)
				{
					PatternAutomation pa = p.Automations[k];
					jw.Class();
					jw.Field("id", pa.ID);
					jw.Field("macro", pa.Macro);
					jw.Field("channel", pa.Channel);
					jw.Array("keys");
					for (int u = 0; u < pa.Keys.Count; u++)
					{
						jw.Class();
						jw.Field("id", pa.Keys[u].ID);
						jw.Field("time", pa.Keys[u].Time);
						jw.Field("value", pa.Keys[u].Value);
						jw.End();
					}
					jw.End();
					jw.End();
				}
				jw.End();

				jw.End();
			}


			jw.End();

			context.Response.Write("g_Patterns = ");
			context.Response.Write(jw.ToString(JSONFormatting.Pretty));
			context.Response.Write(";\n");
		}

		private static void ListMidiDevicesJSON(WebContext context, Song s)
		{
			JSONWriter jw = new JSONWriter();
			jw.Array();

			List<int> ids = MidiWrapper.GetDeviceIDs();

			for (int k = 0; k < ids.Count; k++)
			{
				string nam = MidiWrapper.GetDeviceName(ids[k]);
				jw.Class();
				jw.Field("id", ids[k]);
				jw.Field("name", nam);
				jw.End();
			}

			jw.End();

			context.Response.Write("g_MidiDevices = ");
			context.Response.Write(jw.ToString(JSONFormatting.Pretty));
			context.Response.Write(";\n");
		}

		private static void HandleStaticContentRequest(WebContext context)
		{
			string lp = "static_html" + context.Request.LocalPath;
			// Console.WriteLine("Handling static-content request: " + lp);

			if (Directory.Exists(lp))
			{
				lp += "/index.htm";
			}

			if (File.Exists(lp))
			{

				context.Response.SetHeader("Expires", DateTime.Now.AddDays(-7).ToString("R"));
				context.Response.StatusCode = 200;

				string ext = lp.Substring(lp.LastIndexOf(".") + 1).ToLower();

				Console.WriteLine("ext=\"" + ext + "\"");
				switch (ext)
				{
					case "jpg":
					case "jpeg":
						context.Response.ContentType = "image/jpeg";
						break;
					case "png":
						context.Response.ContentType = "image/png";
						break;
					case "gif":
						context.Response.ContentType = "image/gif";
						break;
					case "js":
						context.Response.ContentType = "text/javascript";
						break;
					case "css":
						context.Response.ContentType = "text/css";
						break;
					case "htm":
						context.Response.ContentType = "text/html";
						break;
					case "txt":
						context.Response.ContentType = "text/plain";
						break;
					default:
						context.Response.ContentType = "application/x-octet-stream";
						break;
				}

				byte[] dum = File.ReadAllBytes(lp);
				Console.WriteLine("sending " + dum.Length + " bytes");
				context.Response.Write(dum);
				// }
			}
			else
			{
				context.Response.StatusCode = 404;
				Console.WriteLine("sending 404");
			}
		}

		private static void HandleRootRequest(WebContext context)
		{
			Console.WriteLine("Handling root-request.");

			string ua = context.Request.GetHeader("User-Agent");
			Console.WriteLine("UserAgent: " + ua);

			string id = "";
			int id_priority = 0;

			for (int j = 0; j < Configuration.Current.UIs.Count; j++)
			{
				if (Configuration.Current.UIs[j].UserAgent != "")
				{
					if (ua.Contains(Configuration.Current.UIs[j].UserAgent))
					{
						id = Configuration.Current.UIs[j].Name;
						id_priority = Configuration.Current.UIs[j].Priority;
					}
				}
			}
			for (int j = 0; j < Configuration.Current.UIs.Count; j++)
			{
				if (Configuration.Current.UIs[j].UserAgent == "")
				{
					if (Configuration.Current.UIs[j].Priority <= id_priority)
					{
						id = Configuration.Current.UIs[j].Name;
						id_priority = Configuration.Current.UIs[j].Priority;
					}
				}
			}

			string sid = SessionManager.CreateID();
			context.Response.StatusCode = (int)HttpStatusCode.Redirect;
			context.Response.ContentType = "text/html";
			context.Response.SetHeader("Location", "uis/" + id + "/index.htm#SESSION=" + sid);
			context.Response.Write("<html><body></body></html>");
		}


		private static void HandleAjaxRequest(WebContext context)
		{
			context.Response.ContentType = "text/plain";
			context.Response.SetHeader("Expires", DateTime.Now.AddYears(-1).ToString("R"));
			
			 Console.WriteLine("Handling ajaxrequest: " + context.Request.LocalPath + ", querystring: " + context.Request.QueryString + ", postdata: " + context.Request.PostData );
			// context.Response.Write("got ajaxrequest: " + context.Request.LocalPath + ", querystring: " + context.Request.QueryString + ", postdata: " + context.Request.PostData + "</p>");

			if (context.Request.HasParameter("getinstrumentlist"))
				GetInstrumentList(context);

			else if (context.Request.HasParameter("getautomationchannelinterpolation"))
				GetAutomationChannelInterpolation(context);

			else if (context.Request.HasParameter("cuepattern"))
				CuePattern(context);

			else if (context.Request.HasParameter("getinstrument"))
				GetInstrument(context);

			else if (context.Request.HasParameter("getmididevicelist"))
				GetMidiDeviceList(context);

			else if (context.Request.HasParameter("changepatterninstrument"))
				ChangePatternInstrument(context);

			else if (context.Request.HasParameter("createinstrument"))
				CreateInstrument(context);

			else if (context.Request.HasParameter("deleteinstrument"))
				DeleteInstrument(context);

			else if (context.Request.HasParameter("updateinstrument"))
				UpdateInstrument(context);

			else if (context.Request.HasParameter("getpatternlist"))
				GetPatternList(context);

			else if (context.Request.HasParameter("gettracklist"))
				GetTrackList(context);

			else if (context.Request.HasParameter("getpattern"))
				GetPattern(context);

			else if (context.Request.HasParameter("renamepattern"))
				RenamePattern(context);

			else if (context.Request.HasParameter("changepatternduration"))
				ChangePatternDuration(context);

			else if (context.Request.HasParameter("clonepattern"))
				ClonePattern(context);

			else if (context.Request.HasParameter("createpattern"))
				CreatePattern(context);

			else if (context.Request.HasParameter("createtrack"))
				CreateTrack(context);

			else if (context.Request.HasParameter("getautomationchannels"))
				GetAutomationChannels(context);

			else if (context.Request.HasParameter("poll"))
				Poll(context);

			else if (context.Request.HasParameter("save"))
				Save(context);

			else if (context.Request.HasParameter("settiming"))
				SetTiming(context);

			else if (context.Request.HasParameter("gettiming"))
				GetTiming(context);

			else if (context.Request.HasParameter("notify"))
				Notify(context);

			else if (context.Request.HasParameter("transposetrack"))
				TransposeTrack(context);

			else if (context.Request.HasParameter("mutetrack"))
				MuteTrack(context);

			else if (context.Request.HasParameter("solotrack"))
				SoloTrack(context);

			else if (context.Request.HasParameter("changetrackvolume"))
				ChangeTrackVolume(context);
		}

		private static void ChangePatternInstrument(WebContext context)
		{

			string oldpat = context.Request.GetParameter("changepatterninstrument");
			string sid = context.Request.GetParameter("session");
			string insid = context.Request.GetParameter("instrument");
			Song s = State.CurrentSong;
			Pattern p = s.GetPatternByID(oldpat);
			p.InstrumentID = insid;
			// s.Patterns.Add(p);

			Notifications.QueueToAll(sid, "pattern-changed|" + oldpat);

			s.SaveToFile("testsong-temp.xml");

			context.Response.ContentType = "text/plain";
			context.Response.Write("OK");
		}

		private static void GetTiming(WebContext context)
		{
			// throw new NotImplementedException();

			string sid = context.Request.GetParameter("gettiming");

			Song s = State.CurrentSong;

			JSONWriter jw = new JSONWriter();
			jw.Class();
			jw.Field("bpm", s.BPM);
			jw.Field("measure1", s.Beats1);
			jw.Field("measure2", s.Beats2);
			jw.End();

			context.Response.ContentType = "text/javascript";
			context.Response.Write(jw.ToString());
		}

		private static void SetTiming(WebContext context)
		{
			// throw new NotImplementedException();

			string sid = context.Request.GetParameter("settiming");

			int bpm = context.Request.GetIntParameter("bpm");
			int m1 = context.Request.GetIntParameter("measure1");
			int m2 = context.Request.GetIntParameter("measure2");

			Song s = State.CurrentSong;
			s.BPM = bpm;
			s.Beats1 = m1;
			s.Beats2 = m2;

			context.Response.ContentType = "text/plain";
			context.Response.Write("OK");

			Notifications.QueueToAll(sid, "timing-changed|");
		}

		private static void CuePattern(WebContext context)
		{
			// throw new NotImplementedException();

			string sid = context.Request.GetParameter("cuepattern");

			string tid = context.Request.GetParameter("track");
			string pid = context.Request.GetParameter("pattern");

			Song s = State.CurrentSong;
			SongTrack st = s.GetTrackByID(tid);
			if (st != null)
			{
				st.CuedPatternID = pid;
				st.LivePatternID = pid;
			}
			context.Response.ContentType = "text/plain";
			context.Response.Write("OK");

			Notifications.QueueToAll(sid, "track-changed|" + tid);
			Notifications.QueueToAll(sid, "tracklist-changed|" + tid);

		}

		private static void SoloTrack(WebContext context)
		{
			// throw new NotImplementedException();

			string tid = context.Request.GetParameter("solotrack");
			int solo = context.Request.GetIntParameter("solo");
			string sid = context.Request.GetParameter("session");

			Song s = State.CurrentSong;
			SongTrack st = s.GetTrackByID(tid);
			if (st != null)
			{
				st.Solo = (solo == 1);
			}
			context.Response.ContentType = "text/plain";
			context.Response.Write("OK");

			Notifications.QueueToAll(sid, "track-changed|" + tid);
			Notifications.QueueToAll(sid, "tracklist-changed|" + tid);
		}

		private static void MuteTrack(WebContext context)
		{
			// throw new NotImplementedException();

			string tid = context.Request.GetParameter("mutetrack");
			int muted = context.Request.GetIntParameter("mute");
			string sid = context.Request.GetParameter("session");

			Song s = State.CurrentSong;
			SongTrack st = s.GetTrackByID(tid);
			if (st != null)
			{
				st.Muted = (muted == 1);
			}
			context.Response.ContentType = "text/plain";
			context.Response.Write("OK");

			Notifications.QueueToAll(sid, "track-changed|" + tid);
			Notifications.QueueToAll(sid, "tracklist-changed|" + tid);
		}

		private static void TransposeTrack(WebContext context)
		{
			// throw new NotImplementedException();

			string tid = context.Request.GetParameter("transposetrack");
			int transpose = context.Request.GetIntParameter("transpose");
			string sid = context.Request.GetParameter("session");

			Song s = State.CurrentSong;
			SongTrack st = s.GetTrackByID(tid);
			if (st != null)
			{
				st.Transpose = transpose;
			}
			context.Response.ContentType = "text/plain";
			context.Response.Write("OK");

			Notifications.QueueToAll(sid, "track-changed|" + tid);
			Notifications.QueueToAll(sid, "tracklist-changed|" + tid);
		}

		private static void ChangeTrackVolume(WebContext context)
		{
			// throw new NotImplementedException();

			string tid = context.Request.GetParameter("changetrackvolume");
			int newvol = context.Request.GetIntParameter("volume");
			string sid = context.Request.GetParameter("session");

			Song s = State.CurrentSong;
			SongTrack st = s.GetTrackByID(tid);
			if (st != null)
			{
				st.Volume = newvol;
			}
			context.Response.ContentType = "text/plain";
			context.Response.Write("OK");

			Notifications.QueueToAll(sid, "track-changed|" + tid);
			Notifications.QueueToAll(sid, "tracklist-changed|" + tid);
		}

		private static void GetMidiDeviceList(WebContext context)
		{
			string sid = context.Request.GetParameter("getmididevicelist");

			JSONWriter jw = new JSONWriter();
			jw.Class();
			jw.Array("devices");

			List<int> ids = MidiWrapper.GetDeviceIDs();

			for (int k = 0; k < ids.Count; k++)
			{
				string nam = MidiWrapper.GetDeviceName(ids[k]);
				jw.Class();
				jw.Field("id", ids[k]);
				jw.Field("name", nam);
				jw.End();
			}
			jw.End();
			jw.End();
			context.Response.ContentType = "text/javascript";
			context.Response.Write(jw.ToString());
		}

		private static void GetTrackList(WebContext context)
		{
			string sid = context.Request.GetParameter("gettracklist");
			Song s = State.CurrentSong;
			JSONWriter jw = new JSONWriter();
			jw.Class();
			jw.Array("tracks");
			for (int k = 0; k < s.Tracks.Count; k++)
			{
				SongTrack tr = s.Tracks[k];
				//	if (tr.ID == insid)
				{
					jw.Class();
					jw.Field("id", tr.ID);
					jw.Field("name", tr.Name);

					if (tr.CurrentPatternID != null)
						jw.Field("pattern", tr.CurrentPatternID);
					else
						jw.Field("pattern", "");

					if (tr.CuedPatternID != null)
						jw.Field("cued", tr.CuedPatternID);
					else
						jw.Field("cued", "");

					jw.Field("volume", tr.Volume);
					jw.Field("transpose", tr.Transpose);
					jw.Field("mute", tr.Muted ? 1 : 0);
					jw.Field("solo", tr.Solo ? 1 : 0);
					jw.End();
					//					jw.Field("type", ins.Type);
					//					jw.Field("mididevice", ins.MidiDevice);
					//					jw.Field("midichannel", ins.MidiChannel);
					//					jw.Field("midipatch", ins.MidiPatch);
				}
			}
			jw.End();
			jw.End();
			context.Response.ContentType = "text/javascript";
			context.Response.Write(jw.ToString());
		}


		private static void ChangePatternDuration(WebContext context)
		{
			string oldpat = context.Request.GetParameter("changepatternduration");
			string sid = context.Request.GetParameter("session");
			int dur = context.Request.GetIntParameter("duration");
			Song s = State.CurrentSong;

			Pattern p = s.GetPatternByID(oldpat);
			p.Duration = dur;
			// s.Patterns.Add(p);

			Notifications.QueueToAll(sid, "pattern-changed|" + oldpat);

			s.SaveToFile("testsong-temp.xml");
		}

		private static void Save(WebContext context)
		{
			if (context == null) throw new ArgumentNullException("context");
			State.CurrentSong.SaveToFile("testsong-saved.xml");
		}

		private static void GetInstrumentList(WebContext context)
		{
			Song s = State.CurrentSong;
			JSONWriter jw = new JSONWriter();
			jw.Class();
			jw.Array("instruments");
			for (int k = 0; k < s.Instruments.Count; k++)
			{
				Instrument ins = s.Instruments[k];

				jw.Class();
				jw.Field("id", ins.ID);
				jw.Field("name", ins.Name);
				jw.Field("type", ins.Type);
				jw.End();
			}
			jw.End();
			jw.End();
			context.Response.ContentType = "text/javascript";
			context.Response.Write(jw.ToString());
		}

		private static void GetInstrument(WebContext context)
		{
			string insid = context.Request.GetParameter("getinstrument");
			Song s = State.CurrentSong;
			JSONWriter jw = new JSONWriter();
			jw.Class();
			for (int k = 0; k < s.Instruments.Count; k++)
			{
				Instrument ins = s.Instruments[k];
				if (ins.ID == insid)
				{
					jw.Field("id", ins.ID);
					jw.Field("name", ins.Name);
					jw.Field("type", ins.Type);
					jw.Field("mididevice", ins.MidiDevice);
					jw.Field("midichannel", ins.MidiChannel);
					jw.Field("midipatch", ins.MidiPatch);
					jw.Field("defaultdeviceui", ins.DefaultDeviceUI);
					jw.Field("defaulteditorui", ins.DefaultEditorUI);

				}
			}
			jw.End();
			context.Response.ContentType = "text/javascript";
			context.Response.Write(jw.ToString());
		}

		private static void CreateInstrument(WebContext context)
		{
			string insid = context.Request.GetParameter("createinstrument");
			string sid = context.Request.GetParameter("session");
			string name = context.Request.GetParameter("name");

			Song s = State.CurrentSong;
			Instrument ins = new Instrument();
			ins.ID = insid;
			ins.Name = name;
			s.Instruments.Add(ins);

			Console.WriteLine("Creating instrument: " + name);

			s.SaveToFile("testsong-temp.xml");

			Notifications.QueueToAll(sid, "instrument-created|" + insid);
			Notifications.QueueToAll(sid, "instrumentlist-changed|" + insid);

			context.Response.ContentType = "text/plain";
			context.Response.Write("OK");
		}

		private static void DeleteInstrument(WebContext context)
		{
			string insid = context.Request.GetParameter("deleteinstrument");
			int idx = -1;
			Song s = State.CurrentSong;
			for (int k = 0; k < s.Instruments.Count; k++)
			{
				Instrument ins = s.Instruments[k];
				if (ins.ID == insid)
				{
					idx = k;
				}
			}

			if (idx != -1)
				s.Instruments.RemoveAt(idx);

			s.SaveToFile("testsong-temp.xml");

			string sid = context.Request.GetParameter("session");
			Notifications.QueueToAll(sid, "instrument-deleted|" + insid);
			Notifications.QueueToAll(sid, "instrumentlist-changed|" + insid);

			context.Response.ContentType = "text/plain";
			context.Response.Write("OK");

		}

		private void DeletePattern(WebContext context) //Not in use right now (2009-01-31)
		{
			string patid = context.Request.GetParameter("deletepattern");
			int idx = -1;
			Song s = State.CurrentSong;
			for (int k = 0; k < s.Patterns.Count; k++)
			{
				Pattern pat = s.Patterns[k];
				if (pat.ID == patid)
				{
					idx = k;
				}
			}

			if (idx != -1)
				s.Patterns.RemoveAt(idx);

			s.SaveToFile("testsong-temp.xml");

			string sid = context.Request.GetParameter("session");
			Notifications.QueueToAll(sid, "pattern-deleted|" + patid);
			Notifications.QueueToAll(sid, "patternlist-changed|" + patid);

			context.Response.ContentType = "text/plain";
			context.Response.Write("OK");

		}

		private static void UpdateInstrument(WebContext context)
		{
			string insid = context.Request.GetParameter("updateinstrument");

			string name = context.Request.GetParameter("name");
			string type = context.Request.GetParameter("type");

			int midichannel = context.Request.GetIntParameter("midichannel");
			int mididevice = context.Request.GetIntParameter("mididevice");
			int midipatch = context.Request.GetIntParameter("midipatch");

			string defaulteditor = context.Request.GetParameter("defaulteditorui");
			string defaultdevice = context.Request.GetParameter("defaultdeviceui");

			Song s = State.CurrentSong;
			for (int k = 0; k < s.Instruments.Count; k++)
			{
				Instrument ins = s.Instruments[k];
				if (ins.ID == insid)
				{
					ins.Name = name;
					ins.Type = type;
					ins.MidiChannel = midichannel;
					ins.MidiDevice = mididevice;
					ins.MidiPatch = midipatch;
					ins.DefaultEditorUI = defaulteditor;
					ins.DefaultDeviceUI = defaultdevice;
				}
			}

			s.SaveToFile("testsong-temp.xml");

			string sid = context.Request.GetParameter("session");
			Notifications.QueueToAll(sid, "instrument-updated|" + insid);

			context.Response.ContentType = "text/plain";
			context.Response.Write("OK");

		}

		private static void GetPatternList(WebContext context)
		{
			// string ins = context.Request.GetParameter("getpatternlist");
			Song s = State.CurrentSong;
			JSONWriter jw = new JSONWriter();
			jw.Class();
			jw.Array("patterns");
			for (int k = 0; k < s.Patterns.Count; k++)
			{
				Pattern p = s.Patterns[k];
				// if (p.InstrumentID == ins)
				{
					jw.Class();
					jw.Field("id", p.ID);
					jw.Field("name", p.Name);
					jw.End();
				}
			}
			jw.End();
			jw.End();
			context.Response.ContentType = "text/javascript";
			context.Response.Write(jw.ToString());
		}

		private static void GetPattern(WebContext context)
		{
			string pat = context.Request.GetParameter("getpattern");
			Song s = State.CurrentSong;
			Pattern p = s.GetPatternByID(pat);

			JSONWriter jw = new JSONWriter();
			jw.Class();
			jw.Field("id", p.ID);
			jw.Field("duration", p.Duration);
			jw.Field("name", p.Name);
			jw.Array("notes");
			for (int k = 0; k < p.Notes.Count; k++)
			{
				PatternNote n = p.Notes[k];
				jw.Class();
				jw.Field("id", n.ID);
				jw.Field("from", n.From);
				jw.Field("to", n.To);
				jw.Field("note", n.Note);
				jw.Field("velocity", n.Velocity);
				jw.End();
			}
			jw.End();
			jw.Array("automations");
			for (int k = 0; k < p.Automations.Count; k++)
			{
				PatternAutomation am = p.Automations[k];
				jw.Class();
				jw.Field("id", am.ID);
				jw.Field("channel", am.Channel);
				jw.Field("macro", am.Macro);
				jw.Array("points");
				for (int j = 0; j < am.Keys.Count; j++)
				{
					PatternAutomationKey amk = am.Keys[j];
					jw.Class();
					jw.Field("id", amk.ID);
					jw.Field("time", amk.Time);
					jw.Field("value", amk.Value);
					jw.End();
				}
				jw.End();
				jw.End();
			}
			jw.End();
			jw.End();
			context.Response.ContentType = "text/javascript";
			context.Response.Write(jw.ToString());
		}

		private static void Notify(WebContext context)
		{
			string sid = context.Request.GetParameter("session");
			string n = context.Request.GetParameter("notify");
			Console.WriteLine("notify, n=" + n);
			if (n == "addnote")
			{
				string pat = context.Request.GetParameter("pattern");
				string id = context.Request.GetParameter("id");
				int from = context.Request.GetIntParameter("from");
				int to = context.Request.GetIntParameter("to");
				int note = context.Request.GetIntParameter("note");
				int vel = context.Request.GetIntParameter("velocity");

				Notifications.QueueToAll(sid, "note-added|" + id + "|" + pat);
				Notifications.QueueToAll(sid, "pattern-changed|" + pat);

				// string pat = "pat0";
				Song s = State.CurrentSong;
				Pattern p = s.GetPatternByID(pat);
				p.Notes.Add(new PatternNote(id, from, to, note, vel));
				State.CurrentSong.SaveToFile("testsong-autosave.xml");
			}
			else if (n == "movenote" || n == "modifynote")
			{
				string pat = context.Request.GetParameter("pattern");// "dummy000";
				string id = context.Request.GetParameter("id");

				int from = context.Request.GetIntParameter("from", -1);
				int to = context.Request.GetIntParameter("to", -1);
				int note = context.Request.GetIntParameter("note", -1);
				int vel = context.Request.GetIntParameter("velocity", -1);

				Notifications.QueueToAll(sid, "note-updated|" + id + "|" + pat);
				Notifications.QueueToAll(sid, "pattern-changed|" + pat);

				Song s = State.CurrentSong;
				Pattern p = s.GetPatternByID(pat);
				PatternNote nt = p.GetNoteByID(id);
				if (nt != null)
				{
					if (from != -1)
						nt.From = from;

					if (to != -1)
						nt.To = to;

					if (note != -1)
						nt.Note = note;

					if (vel != -1)
						nt.Velocity = vel;
				}

				State.CurrentSong.SaveToFile("testsong-autosave.xml");
			}
			else if (n == "deletenote")
			{
				string pat = context.Request.GetParameter("pattern");// "dummy000";
				string id = context.Request.GetParameter("id");

				Notifications.QueueToAll(sid, "note-deleted|" + id + "|" + pat);
				Notifications.QueueToAll(sid, "pattern-changed|" + pat);

				Song s = State.CurrentSong;
				Pattern p = s.GetPatternByID(pat);
				p.RemoveNoteByID(id);

				State.CurrentSong.SaveToFile("testsong-autosave.xml");
			}
			else if (n == "keyroll")
			{
				// string pat = context.Request.GetParameter("pattern");// "dummy000";
				string insid = context.Request.GetParameter("instrument");
				int note = context.Request.GetIntParameter("note");

				Song s = State.CurrentSong;
				Instrument ins = s.GetInstrumentByID(insid);
				if (ins != null)
				{
					string state = context.Request.GetParameter("keystate");
					if (state == "down")
					{
						Console.WriteLine("Keyroll down, note=" + note);
						MidiWrapper.QueueNoteOn(ins.MidiDevice, ins.MidiChannel, note, 100);
					}
					else if (state == "up")
					{
						Console.WriteLine("Keyroll up, note=" + note);
						MidiWrapper.QueueNoteOff(ins.MidiDevice, ins.MidiChannel, note);
					}
				}
			}
			else if (n == "createautomationkey")
			{
				string patid = context.Request.GetParameter("pattern");
				string keyid = context.Request.GetParameter("id");
				int time = context.Request.GetIntParameter("time");
				int value = context.Request.GetIntParameter("value");
				int chan = context.Request.GetIntParameter("channel");

				Song s = State.CurrentSong;
				Pattern pat = s.GetPatternByID(patid);
				if (pat != null)
				{
					PatternAutomation aut = pat.GetAutomationByChannel(chan);
					if (aut == null)
					{
						aut = new PatternAutomation();
						aut.Macro = "";
						aut.Channel = chan;
						aut.ID = "aut" + DateTime.Now.Ticks;
						pat.Automations.Add(aut);
					}

					PatternAutomationKey key = new PatternAutomationKey();
					key.ID = keyid;
					key.Time = time;
					key.Value = value;
					aut.Keys.Add(key);

					aut.SortKeys();
				}
			}
			else if (n == "deleteautomationkey")
			{
				string patid = context.Request.GetParameter("pattern");
				string keyid = context.Request.GetParameter("id");
				int chan = context.Request.GetIntParameter("channel");

				Song s = State.CurrentSong;
				Pattern pat = s.GetPatternByID(patid);
				if (pat != null)
				{
					PatternAutomation aut = pat.GetAutomationByChannel(chan);
					if (aut != null)
					{
						int idx = -1;
						for (int k = 0; k < aut.Keys.Count; k++)
							if (aut.Keys[k].ID == keyid)
								idx = k;

						if (idx != -1)
							aut.Keys.RemoveAt(idx);

						aut.SortKeys();
					}
				}
			}
			else if (n == "updateautomationkey")
			{
				string patid = context.Request.GetParameter("pattern");
				string keyid = context.Request.GetParameter("id");
				int newtime = context.Request.GetIntParameter("time");
				int newvalue = context.Request.GetIntParameter("value");
				int chan = context.Request.GetIntParameter("channel");

				Song s = State.CurrentSong;
				Pattern pat = s.GetPatternByID(patid);
				if (pat != null)
				{
					PatternAutomation aut = pat.GetAutomationByChannel(chan);
					if (aut != null)
					{
						for (int k = 0; k < aut.Keys.Count; k++)
						{
							if (aut.Keys[k].ID == keyid)
							{
								aut.Keys[k].Time = newtime;
								aut.Keys[k].Value = newvalue;
							}
						}

						aut.SortKeys();
					}
				}

			}
			context.Response.ContentType = "text/plain";
			context.Response.Write("OK");

		}

		private static void Poll(WebContext context)
		{
			string sid = context.Request.GetParameter("poll");
			SessionManager.KeepAlive(sid);
			string msg = Notifications.Poll(sid);
			// AjaxUtilities.ReturnString(msg);
			context.Response.ContentType = "text/plain";
			context.Response.Write(msg);

		}

		private static void PollLongRunning(WebContext context)
		{
			string sid = context.Request.GetParameter("poll");
			SessionManager.KeepAlive(sid);
			context.Response.ContentType = "text/plain";
			DateTime tstimeout = DateTime.Now.AddSeconds(20);
			DateTime ts = DateTime.Now;
			string lastmsg = "";
			do
			{
				ts = DateTime.Now;
				lastmsg = Notifications.Poll(sid);
				Thread.Sleep(20);
			} while (ts < tstimeout && lastmsg == "");
			context.Response.Write(lastmsg);
		}

		private static void GetAutomationChannels(WebContext context)
		{
			JSONWriter jw = new JSONWriter();
			jw.Class();
			jw.Array("channels");
			for (int k = 0; k < 127; k++)
			{
				jw.Class();
				jw.Field("id", k);
				jw.Field("name", MidiWrapper.GetControllerName( k ));
				jw.End();
			}
			jw.End();
			jw.End();
			context.Response.ContentType = "text/javascript";
			context.Response.Write(jw.ToString());
		}

		private static void GetAutomationChannelInterpolation(WebContext context)
		{
			string patid = context.Request.GetParameter("pattern");
			int chan = context.Request.GetIntParameter("channel");

			Song s = State.CurrentSong;
			Pattern pat = s.GetPatternByID(patid);
			PatternAutomation aut = null;
			if (pat != null)
			{
				aut = pat.GetAutomationByChannel(chan);
			}

			JSONWriter jw = new JSONWriter();
			jw.Class();
			jw.Array("channelinterpolation");
			if (aut != null)
			{
				const int res = 200;
				for (int k = 0; k < res; k++)
				{
					if (k > 0)
						jw.WriteCustom(", ");
					float t = (float)(k * pat.Duration * 100.0f) / (float)res;
					int v = (int)aut.GetValueAt(t);
					jw.WriteCustom(v.ToString());
				}
			}
			jw.End();
			jw.Array("keys");
			if (aut != null)
			{
				for (int k = 0; k < aut.Keys.Count; k++)
				{
					jw.Class();
					jw.Field("time", aut.Keys[k].Time);
					jw.Field("value", aut.Keys[k].Value);
					jw.End();
				}
			}
			jw.End();
			jw.End();
			
			context.Response.ContentType = "text/javascript";
			context.Response.Write(jw.ToString(JSONFormatting.Pretty));
		}

		private static void CreateTrack(WebContext context)
		{
			string newid = context.Request.GetParameter("createpattern");
			string sid = context.Request.GetParameter("session");
			string nam = context.Request.GetParameter("name");

			Console.WriteLine("Creating track: " + nam);

			Song s = State.CurrentSong;

			SongTrack p = new SongTrack();
			p.ID = newid;
			p.Name = nam;
			s.Tracks.Add(p);

			Notifications.QueueToAll(sid, "track-created|" + newid);
			Notifications.QueueToAll(sid, "tracklist-changed|" + newid);

			s.SaveToFile("testsong-temp.xml");

			context.Response.ContentType = "text/plain";
			context.Response.Write("OK");

		}
		private static void CreatePattern(WebContext context)
		{
			string newid = context.Request.GetParameter("createpattern");
			string sid = context.Request.GetParameter("session");
			string nam = context.Request.GetParameter("name");

			Console.WriteLine("Creating pattern: " + nam);

			Song s = State.CurrentSong;

			Pattern p = new Pattern();
			p.ID = newid;
			p.Name = nam;
			s.Patterns.Add(p);

			Notifications.QueueToAll(sid, "pattern-created|" + newid);
			Notifications.QueueToAll(sid, "patternlist-changed|" + newid);

			s.SaveToFile("testsong-temp.xml");

			context.Response.ContentType = "text/plain";
			context.Response.Write("OK");

		}

		private static void ClonePattern(WebContext context)
		{
			string newid = context.Request.GetParameter("clonepattern");
			string sid = context.Request.GetParameter("session");
			string pat = context.Request.GetParameter("pattern");
			string nam = context.Request.GetParameter("name");

			Song s = State.CurrentSong;

			Pattern p = s.GetPatternByID(pat);
			p = p.Clone();
			p.ID = newid;
			p.Name = nam;
			s.Patterns.Add(p);

			Console.WriteLine("Cloned pattern: " + nam);

			Notifications.QueueToAll(sid, "pattern-changed|" + newid);
			Notifications.QueueToAll(sid, "patternlist-changed|" + newid);


			s.SaveToFile("testsong-temp.xml");

			context.Response.ContentType = "text/plain";
			context.Response.Write("OK");
		}

		private static void RenamePattern(WebContext context)
		{
			string oldpat = context.Request.GetParameter("renamepattern");
			string sid = context.Request.GetParameter("session");
			string nam = context.Request.GetParameter("name");
			Song s = State.CurrentSong;

			Pattern p = s.GetPatternByID(oldpat);
			p.Name = nam;

			Console.WriteLine("Renamed pattern: " + nam);

			Notifications.QueueToAll(sid, "pattern-renamed|" + oldpat);
			Notifications.QueueToAll(sid, "patternlist-changed|" + oldpat);

			s.SaveToFile("testsong-temp.xml");

			context.Response.ContentType = "text/plain";
			context.Response.Write("OK");
		}

	}
}
